#ifndef __SPP_H__
#define __SPP_H__
#include "ad.h"
#include <iostream>
#define SECURITY 0

template <typename T, typename A = ADS<T> >
class SPP {
	private :
      unsigned int * nbp ;
	protected :
		T * ptr ;
		inline void Connect (T*a, unsigned int * n) throw (std::bad_alloc) ;
		
	public :
	/* Constructeur par défaut */
		inline SPP () ;
	/* Constructeur par copie */
		inline SPP (const SPP<T,A> &) throw (std::bad_alloc);
	/* Constructeur par duplication d'objet */
		inline SPP (const T&) throw (std::bad_alloc);

	/* Destructeur */
		inline ~SPP () ;
	
	/* Accesseur en écriture */
		inline T& operator *() throw (std::string) ;
	/* Accesseur en lecture */
		inline const T& operator *() const throw (std::string) ;
	/* Reaffecteur */
		inline const SPP & operator= (const SPP &) throw (std::bad_alloc) ;
	
	/* Transtipage */
		template <typename U> inline const U & ConvObj () const throw (std::string, std::bad_cast);
		template <typename U> inline U & ConvObj () throw (std::string, std::bad_cast) ;
	
	/* Opérateur bool */
		inline operator bool () const ;
		inline void Deconnect () ;
		inline void NewObj (T &) throw (std::bad_alloc);
};

/* Constructeur par défaut */
template <typename T, typename A>
SPP<T,A>::SPP () : nbp (NULL), ptr (NULL){}

/* Constructeur par copie */
template <typename T, typename A>
SPP<T,A>::SPP (const SPP<T,A> & spp) throw (std::bad_alloc) {
   Connect (spp.ptr, spp.nbp) ;
}

/* Constructeur par copie d'objet */
template <typename T, typename A>
SPP<T,A>::SPP (const T& t) throw (std::bad_alloc) {
   Connect (A::New(t), NULL) ;
}

/* Destructeur */
template <typename T, typename A>
SPP<T,A>::~SPP() {
	Deconnect() ;
}

/* Opérateur * en écriture */
template <typename T, typename A>
T& SPP<T,A>::operator* () throw (std::string) {
#if SECURITY
	if (!ptr)
		throw std::string ("Pointeur vide") ;
#endif
	return *ptr ;
}

/* Opérateur * en lecture seule */
template <typename T, typename A>
const T& SPP<T,A>::operator* () const throw (std::string) {
#if SECURITY
	if (!ptr)
		throw std::string ("Pointeur vide") ;
#endif
	return *ptr ;
}

/* Réaffecteur */
template <typename T, typename A>
const SPP<T,A> & SPP<T,A>::operator= (const SPP<T,A> & sp) throw (std::bad_alloc) {
	if (&sp==this)
	   return *this ;
	
	Deconnect() ;
    Connect (sp.ptr, sp.nbp) ;
	
	return *this ;
}

template <typename T, typename A> // private
void SPP<T,A>::Connect (T * a , unsigned int * n) throw (std::bad_alloc) {
//a & n tous 2 non nuls 
   //partage
   if (a && n){
      ptr = a ;
      nbp = n ;
      (*nbp)++ ;
   }
   //a & n tous 2 nuls 
   // état vide
   else if (!a && !n){
      ptr = NULL ;
      nbp = NULL ;
   }
//seul n nul 
   // alloc nbp à 1
   else {
      ptr = a ;
      nbp = new unsigned int (1) ;
   }
}

/* Déconnexion */
template <typename T, typename A>
void SPP<T,A>::Deconnect () {
	if (ptr){
		(*nbp)-- ;
		if ((*nbp)==0){
			A::Free(ptr) ;
		   delete nbp ;
		}
		ptr = NULL;
		nbp = NULL;
	}
}

/* New Object */
template <typename T, typename A>
inline void SPP<T,A>::NewObj (T& t) throw (std::bad_alloc){
   Deconnect () ;
   Connect (A::New (t), NULL) ;
}

template <typename T, typename A> template <typename U>
const U & SPP<T,A>::ConvObj() const throw (std::string, std::bad_cast) {
#if SECURITY
	if (!ptr)
		throw std::string ("Pointeur vide") ;
#endif
	return *dynamic_cast<U*> (ptr) ; // rediffuser exception
}

template <typename T, typename A> template <typename U>
U & SPP<T,A>::ConvObj() throw (std::string, std::bad_cast) {
#if SECURITY
	if (!ptr)
		throw std::string ("Pointeur vide") ;
#endif
	return *dynamic_cast<U*> (ptr) ; // rediffuser exception
}

/* Opérateur bool */
template <typename T, typename A>
inline SPP<T,A>::operator bool () const {
	return ptr ;
}

#undef SECURITY
#endif

